/*
 * Copyright (c) 2009 DeledImporter
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'Pyro Games' nor the names of its contributors 
 *   may be used to endorse or promote products derived from this software 
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package net.pyrogames.deled.importer;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import net.pyrogames.deled.geometry.*;
import net.pyrogames.deled.light.*;
import net.pyrogames.deled.material.*;
import net.pyrogames.deled.material.DXSLayer.BlendType;
import net.pyrogames.deled.material.DXSLayer.MaterialType;
import net.pyrogames.deled.math.*;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * <code>DXSImporter</code> uses a DOM XML parser to read
 * XML dxs files and creates an internal representation of dxs objects
 * which are then passed to a class that implements <code>{@link Format}</code>.
 * <p>
 * The importer then initializes the format's conversion method to use convert
 * the internal representation into the appropriate representation.
 * <p>
 * Users are able to customize this importer by creating and registering extensions
 * that are called when the importer parses geometric and light information. This
 * allows for custom scene elements and the user to have control over how
 * the importer organizes its internal representation (even the default processing
 * methods can be overridden using extensions).
 * <p>
 * Users can further modify the organization of their models in their custom format
 * implementation.
 * <p>
 * If re-using an instance of this importer, be sure to call the <code>{@link #flush()}</code>
 * and <code>{@link #flushExtensions()}</code> (if not re-using extensions).
 * 
 * 
 * 
 * @author Nicholas Woodfield
 *
 */
public class DXSImporter {
	/** Document root of the XML file */
	private Element docRoot;
	/** Root of the internal representation of the scene */
	private DXSNode sceneRoot;
	/** List of registered extensions */
	private ArrayList<DXSExtension> extensions;
	
	/** All the materials that are used for the model */
	private ArrayList<DXSMaterial> materialList;
	
	/**
	 * Create a new <code>DXSImporter</code>.
	 */
	public DXSImporter(){
		materialList = new ArrayList<DXSMaterial>(1);
		
		sceneRoot = null;
		extensions = new ArrayList<DXSExtension>(1);
	}
	
	/**
	 * Compute vertex normals for the primitive. Currently, for each vertex
	 * we calculate the face normals for adjacent polys and add those up, then
	 * normalize. In the future other normal calculations can be added.
	 * 
	 * @param primitive
	 * 			DXSPrimitive we're looking at.
	 * @param type
	 * 			0 = FaceNormals, 1 = Weight normals
	 */
	private void computeNormals(DXSPrimitive primitive, int type){
		//Loop through all the polys of the primitive and calculate face normals
		for(int i = 0 ; i < primitive.polys.size(); i++){
			DXSPoly poly = primitive.polys.get(i);
			DXSVector3f v1 = poly.vertices.get(2);
			DXSVector3f v2 = poly.vertices.get(1);
			DXSVector3f v3 = poly.vertices.get(0);
			
			//Compute face normal
			DXSVector3f edge1 = v1.subtract(v2);
			DXSVector3f edge2 = v2.subtract(v3);
			poly.faceNormal = edge1.cross(edge2).normalizeLocal();
		}
		if(type == 0){
			for(DXSPoly poly : primitive.polys){
				poly.vertNormals.add(poly.faceNormal);
				poly.vertNormals.add(poly.faceNormal);
				poly.vertNormals.add(poly.faceNormal);
			}
		} else if(type == 1){
			for(int i = 0; i < primitive.polys.size(); i++){
				//Get the current polygon
				DXSPoly poly = primitive.polys.get(i);
				//Look at each vertex of the polygon
				for(int j = 0; j < 3; j++){
					//Make a new vertex normal, initially the poly's face normal
					DXSVector3f n = new DXSVector3f(poly.faceNormal);
					//Get the vid for this vertex
					int vid = poly.vertices.get(j).vid;
					//Now search for all polygons that share this vertex
					for(int k = 0; k < primitive.polys.size(); k++){
						//Ensure we're not looking at the same polys
						if(i != k){
							//Get the other poly
							DXSPoly otherPoly = primitive.polys.get(k);
							//Look at each poly's vertex
							for(DXSVector3f v : otherPoly.vertices){
								//If we have a match, add the face normal
								if(vid == v.vid){
									n.add(otherPoly.faceNormal);
								}
							}
						}
					}
					//Finally normalize
					n.normalizeLocal();
					//Save the normal
					poly.vertNormals.add(n);
				}
			}
		}
	}
	
	/**
	 * Returns a processed material based on its ID from the DXS file.
	 * 
	 * @param id
	 * 		Material ID integer
	 * @return
	 * 		The material
	 */
	private DXSMaterial getMaterialByID(int id){
		for(int i = 0; i < materialList.size(); i++){
			DXSMaterial mat = materialList.get(i);
			if(mat.mID == id){
				return mat;
			}
		}
		return null;
	}
	
	/**
	 * Imports a model from some input stream source and delivers as output,
	 * the model in the specified format.
	 * 
	 * @param input
	 * 		URL to the DXS file
	 * @param format
	 * 		Object that implements the Format interface, we pass the DXS data
	 * 		to that object that then converts it into the appropriate format.
	 * @return
	 * 		The format object with the populated data.
	 */
	public Format<?> importModel(URL input, Format<?> format){
		try {
			//Parse and build the XML document
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document doc = builder.parse(input.openStream());
			doc.normalize();
			docRoot = doc.getDocumentElement();
			
			//Create the scene root
			sceneRoot = new DXSNode(new File(input.getFile()).getName());
			
			//Process Materials of the DXS file
			processMaterials();
			//Process lights of the DXS file
			processLights();
			//Process the Geometry of the DXS file
			processGeometry();
			
		} catch(ParserConfigurationException e){
			System.out.println("Error in parsing file");
		} catch (SAXException e){
			System.out.println("Error in parsing file");
		} catch (IOException e){
			System.out.println("Error in parsing file");
		}
		//Send data to the format to be converted into the specific engine format
		try {
			format.loadData(sceneRoot);
		} catch (ImportException e) {
			e.printStackTrace();
			System.out.println("Error in converting file");
		}
		return format;
	}
	
	/**
	 * Processes the geometry of the DXS file. Each primitive has its own
	 * entry and we first setup a master list of vertices for the primitive.
	 * We then go on to process the polygon list and use the vertex list
	 * to create the UV and coordinate lists.
	 */
	private void processGeometry(){
		//Grab all the primitive entries in the file
		NodeList elements = docRoot.getElementsByTagName("primitive");
		//Go through each primitive entry
		for(int i = 0; i < elements.getLength(); i++){
			//Get the element
			Element dxsElement = (Element) elements.item(i);
			boolean success = false;
			//Hijack parsing and see if this element matches any custom 
			//extension we need to look out for
			for(DXSExtension e : extensions){
				success = e.executeExtension(sceneRoot, dxsElement);
				//We don't need to stay in the for loop if we found one,
				//first come first serve
				if(success){
					break;
				}
			}
			//Process geometry as normal if its not a custom element
			if(!success){
				//Temp nodelist until we can grab the vertex list
				NodeList temp = dxsElement.getElementsByTagName("vertices");
				NodeList vertices = ((Element)temp.item(0)).getElementsByTagName("vertex");
				String name = dxsElement.getAttribute("name");
				DXSPrimitive primitive = new DXSPrimitive(name, vertices.getLength());
				//Process the master list of vertices for the primitive
				for(int j = 0; j < vertices.getLength(); j++){
					Element vertex = (Element) vertices.item(j);
					float x = Float.parseFloat(vertex.getAttribute("x"));
					float y = Float.parseFloat(vertex.getAttribute("y"));
					float z = Float.parseFloat(vertex.getAttribute("z"));
					primitive.vertices[j] = new DXSVector3f(x,y,z);
				}
				//Now process the individual polygons for triangle vertices, UV, and normals
				//Note we reuse the temp nodelist
				temp = dxsElement.getElementsByTagName("poly");
				processPolygons(primitive,temp);
				//Process the primitive, e.g. if we need to break it up by material
				processPrimitive(primitive);
			}
		}
	}
	
	private void processLights(){
		//Grab all the lights in the scene
		NodeList lights = docRoot.getElementsByTagName("light");
		
		//Get the scene ambience
		NodeList ambience = docRoot.getElementsByTagName("ambient");
		Element elem = (Element) ambience.item(0);
		DXSColorRGBA ambient = new DXSColorRGBA();
		ambient.r = Float.parseFloat(elem.getAttribute("r")) / 255;
		ambient.g = Float.parseFloat(elem.getAttribute("g")) / 255;
		ambient.b = Float.parseFloat(elem.getAttribute("b")) / 255;
		
		//Go through each light entry, save even disabled lights
		for(int i = 0; i < lights.getLength(); i++){
			
			//Get the light
			Element light = (Element) lights.item(i);
			boolean success = false;
			//Hijack parsing and see if this element matches any custom 
			//extension we need to look out for
			for(DXSExtension e : extensions){
				success = e.executeExtension(sceneRoot, light);
				//We don't need to stay in the for loop if we found one,
				//first come first serve
				if(success){
					break;
				}
			}
			//Process the light as normal if its not a custom element
			if(!success){
	
				//Get if its enabled or not
				boolean active = Boolean.valueOf(light.getAttribute("active"));
				
				//Get if it casts shadows or not
				boolean castShadows = Boolean.valueOf(light.getAttribute("castShadows"));
				
				//Get the name of the light
				String name = light.getAttribute("name");
				
				//Get the type of light
				String type = light.getAttribute("type");
				
				//Get the diffuse color
				NodeList diffList = light.getElementsByTagName("diffuse");
				Element diff = (Element) diffList.item(0);
				DXSColorRGBA diffuse = new DXSColorRGBA();
				diffuse.r = Float.parseFloat(diff.getAttribute("r")) / 255;
				diffuse.g = Float.parseFloat(diff.getAttribute("g")) / 255;
				diffuse.b = Float.parseFloat(diff.getAttribute("b")) / 255;
				
				//Get the specular color
				NodeList specs = light.getElementsByTagName("specular");
				Element spec = (Element) specs.item(0);
				DXSColorRGBA specular = new DXSColorRGBA();
				specular.r = Float.parseFloat(spec.getAttribute("r")) / 255;
				specular.g = Float.parseFloat(spec.getAttribute("g")) / 255;
				specular.b = Float.parseFloat(spec.getAttribute("b")) / 255;
				
				//Determine the correct type of light
				if(type.equals("point")){
					
					//Get attenuation variables
					NodeList atts = light.getElementsByTagName("attenuation");
					Element attenuation = (Element) atts.item(0);
					float constant = Float.parseFloat(attenuation.getAttribute("constant"));
					float linear = Float.parseFloat(attenuation.getAttribute("linear"));
					float quadratic = Float.parseFloat(attenuation.getAttribute("quadratic"));
					
					//Get the position of the light
					DXSVector3f pos = new DXSVector3f();
					NodeList positions = light.getElementsByTagName("position");
					Element position = (Element) positions.item(0);
					pos.x = Float.parseFloat(position.getAttribute("x"));
					pos.y = Float.parseFloat(position.getAttribute("y"));
					pos.z = Float.parseFloat(position.getAttribute("z"));
					
					//create the point light
					DXSPointLight pLight = new DXSPointLight(name, pos);
					
					//Set attributes
					pLight.setAmbient(ambient);
					pLight.setDiffuse(diffuse);
					pLight.setSpecular(specular);
					pLight.setAttenuate(true);
					pLight.setEnabled(active);
					pLight.setConstant(constant);
					pLight.setLinear(linear);
					pLight.setQuadratic(quadratic);
					pLight.setShadowCaster(castShadows);
					
					//Add the light to the scene
					sceneRoot.attachChild(pLight);
					
				} else if(type.equals("directional")){
					
					//Get the direction of the light
					DXSVector3f dir = new DXSVector3f();
					NodeList directions = light.getElementsByTagName("direction");
					Element direction = (Element) directions.item(0);
					dir.x = Float.parseFloat(direction.getAttribute("x"));
					dir.y = Float.parseFloat(direction.getAttribute("y"));
					dir.z = Float.parseFloat(direction.getAttribute("z"));
					
					//create the spot light
					DXSDirectionalLight dLight = new DXSDirectionalLight(name,dir);
					
					//Set attributes
					dLight.setAmbient(ambient);
					dLight.setDiffuse(diffuse);
					dLight.setSpecular(specular);
					dLight.setEnabled(active);
					dLight.setShadowCaster(castShadows);
					
					//Add the light to the scene
					sceneRoot.attachChild(dLight);
					
				} else if(type.equals("spot")){
					
					//Get attenuation variables
					NodeList atts = light.getElementsByTagName("attenuation");
					Element attenuation = (Element) atts.item(0);
					float constant = Float.parseFloat(attenuation.getAttribute("constant"));
					float linear = Float.parseFloat(attenuation.getAttribute("linear"));
					float quadratic = Float.parseFloat(attenuation.getAttribute("quadratic"));
					
					//Get the position of the light
					DXSVector3f pos = new DXSVector3f();
					NodeList positions = light.getElementsByTagName("position");
					Element position = (Element) positions.item(0);
					pos.x = Float.parseFloat(position.getAttribute("x"));
					pos.y = Float.parseFloat(position.getAttribute("y"));
					pos.z = Float.parseFloat(position.getAttribute("z"));
					
					//Get the direction of the light
					DXSVector3f dir = new DXSVector3f();
					NodeList directions = light.getElementsByTagName("direction");
					Element direction = (Element) directions.item(0);
					dir.x = Float.parseFloat(direction.getAttribute("x"));
					dir.y = Float.parseFloat(direction.getAttribute("y"));
					dir.z = Float.parseFloat(direction.getAttribute("z"));
					
					//Get cone attributes
					NodeList cones = light.getElementsByTagName("cone");
					Element cone = (Element) cones.item(0);
					float innerAngle = Float.parseFloat(cone.getAttribute("inner"));
					float outerAngle = Float.parseFloat(cone.getAttribute("outer"));
					float falloff = Float.parseFloat(cone.getAttribute("falloff"));
					
					//create the spot light
					DXSSpotLight sLight = new DXSSpotLight(name, pos, dir, innerAngle, outerAngle, falloff);
					
					//Set attributes
					sLight.setAmbient(ambient);
					sLight.setDiffuse(diffuse);
					sLight.setSpecular(specular);
					sLight.setAttenuate(true);
					sLight.setEnabled(active);
					sLight.setConstant(constant);
					sLight.setLinear(linear);
					sLight.setQuadratic(quadratic);
					sLight.setShadowCaster(castShadows);
					
					//Add the light to the scene
					sceneRoot.attachChild(sLight);
				}
			}
		}
	}
	
	/**
	 * Processes all of the -used- materials in the DXS file and populates
	 * the material entries with that data.
	 */
	private void processMaterials(){
		//Grab all of the materials in the file
		NodeList materials = docRoot.getElementsByTagName("material");
		//Go through each material entry, and save only used ones
		for(int i = 0; i < materials.getLength(); i++){
			Element tag = (Element) materials.item(i);
			if(tag.getAttribute("used").equals("true")){
				int mID = Integer.parseInt(tag.getAttribute("id"));
				DXSMaterial mat = new DXSMaterial(mID,tag.getAttribute("name"));
				//Parse through layer information
				NodeList layers = tag.getElementsByTagName("layer");
				for(int j = 0; j < layers.getLength(); j++){
					Element layerElement = (Element) layers.item(j);
					String typeAtt = layerElement.getAttribute("type");
					//Ignore the layer if its a lightmap
					if(!typeAtt.equals("lightmap")){
						String blendAtt = layerElement.getAttribute("blend");
						DXSLayer layer = new DXSLayer();
						//Set Material type
						if(typeAtt.equals("color")){
							NodeList colors = layerElement.getElementsByTagName("color");
							Element colorElement = (Element) colors.item(0);
							float r = Float.parseFloat(colorElement.getAttribute("r")) / 255;
							float g = Float.parseFloat(colorElement.getAttribute("g")) / 255;
							float b = Float.parseFloat(colorElement.getAttribute("b")) / 255;
							float a = Float.parseFloat(colorElement.getAttribute("a")) / 255;
							layer.material = MaterialType.Color;
							layer.color = new DXSColorRGBA(r,g,b,a);
						} else if(typeAtt.equals("texture")){
							NodeList textures = layerElement.getElementsByTagName("texture");
							Element texElement = (Element) textures.item(0);
							String filePath = texElement.getAttribute("file");
							int index = filePath.lastIndexOf(File.separatorChar);
							layer.material = MaterialType.Texture;
							layer.texName = filePath.substring(index+1);
						}
						//Set blending
						if(blendAtt.equals("ignore")){
							layer.blend = BlendType.Ignore;
						} else if(blendAtt.equals("replace")){
							layer.blend = BlendType.Replace;
						} else if(blendAtt.equals("add")){
							layer.blend = BlendType.Add;
						} else if(blendAtt.equals("modulate")){
							layer.blend = BlendType.Modulate;
						} else if(blendAtt.equals("alphablend")){
							layer.blend = BlendType.Alphablend;
						}
						mat.layers.add(layer);
						materialList.add(mat);
					}
				}
			}
		}
	}
	
	/**
	 * Processes each poly entry for the primitive, the vertex coordinates,
	 * uv coordinates, and normals.
	 * 
	 * @param primitive
	 * 			DXSPrimitive that we're looking at its poly list
	 * @param polys
	 * 			NodeList of the polys for this specific DXSPrimitive
	 */
	private void processPolygons(DXSPrimitive primitive, NodeList polys){
		//Process each poly entry for the primitive
		for(int i = 0; i < polys.getLength(); i++){
			Element polyElement = (Element) polys.item(i);
			DXSPoly poly = new DXSPoly();
			poly.materialID = Integer.parseInt(polyElement.getAttribute("mid"));
			NodeList vertices = polyElement.getElementsByTagName("vertex");
			//Look at each vertex of the poly
			for(int j = 0; j < vertices.getLength(); j++){
				Element vertex = (Element) vertices.item(j);
				int vid = Integer.parseInt(vertex.getAttribute("vid"));
				float u0 = Float.parseFloat(vertex.getAttribute("u0"));
				float v0 = Float.parseFloat(vertex.getAttribute("v0"));
				DXSVector3f v = new DXSVector3f(primitive.vertices[vid]);
				v.vid = vid;
				//Need to "flip" the v coordinate, on some systems
				//this may need to be turned off.
				DXSVector2f tex = new DXSVector2f(u0,1 - v0);
				tex.vid = vid;
				poly.vertices.add(v);
				poly.texVerts.add(tex);
			}
			primitive.polys.add(poly);
		}
		//Triangulate the poly if needed
		triangulatePolys(primitive);
		//Compute the vertex normals
		computeNormals(primitive,1);
	}
	
	/**
	 * Looks at the populated primitive and determines if we need to break it
	 * up into 2 or more primitives based on the material ID's of its polys.
	 * 
	 * @param primitive
	 * 			The DXSPrimitive we're looking at
	 */
	private void processPrimitive(DXSPrimitive primitive){
		HashMap<Integer,ArrayList<DXSPoly>> buckets = new HashMap<Integer,ArrayList<DXSPoly>>();
		for(DXSPoly p : primitive.polys){
			Integer mid = new Integer(p.materialID);
			ArrayList<DXSPoly> polys;
			if(buckets.containsKey(mid)){
				polys = buckets.get(mid);
				polys.add(p);
			} else {
				polys = new ArrayList<DXSPoly>();
				polys.add(p);
				buckets.put(mid, polys);
			}
		}
		Iterator<ArrayList<DXSPoly>> iterator = buckets.values().iterator();
		int i = 0;
		while(iterator.hasNext()){
			String name = primitive.getName();
			//Only assign additional information to the name if
			//we actually split up the polygons
			if(i > 0){
				name += "-" + i;
			}
			DXSPrimitive prim = new DXSPrimitive(name,primitive.vertices.length);
			prim.vertices = primitive.vertices.clone();
			prim.polys = iterator.next();
			//Already sorted the matID's, so we just need to grab the first one
			prim.material = getMaterialByID(prim.polys.get(0).materialID);
			sceneRoot.attachChild(prim);
			i++;
		}
	}
	
	/**
	 * Triangulate polys in the primitive. It will take the data of a poly, clone it,
	 * and ensure that each poly element is just a triangle. This only works
	 * if a polygon can be triangulated and is convex. This is usually a given 
	 * for correct 3D geometry.
	 * 
	 * @param primitive
	 * 			DXSPrimitive we're looking at
	 */
	private void triangulatePolys(DXSPrimitive primitive){
		ArrayList<DXSPoly> newPolys = new ArrayList<DXSPoly>();
		//Look at each poly in the primitive
		for(int i = 0; i < primitive.polys.size(); i++){
			DXSPoly poly = primitive.polys.get(i);
			ArrayList<DXSVector3f> vertices = poly.vertices;
			//Triangulate only if the poly isn't a triangle
			if(vertices.size() > 3){
				//Start with the first vertex being our pivot
				DXSVector3f v0 = poly.vertices.get(0);
				for(int v = 1; v < vertices.size()-1; v++){
					//Create a new poly with the same material id
					DXSPoly newPoly = new DXSPoly();
					newPoly.materialID = poly.materialID;
					//Add the first vertex
					newPoly.vertices.add(new DXSVector3f(v0));
					newPoly.texVerts.add(new DXSVector2f(poly.getTexVertByID(v0.vid)));
					//Add the ith vertex
					newPoly.vertices.add(new DXSVector3f(vertices.get(v)));
					newPoly.texVerts.add(new DXSVector2f(poly.getTexVertByID(vertices.get(v).vid)));
					//Add the i+1th vertex
					newPoly.vertices.add(new DXSVector3f(vertices.get(v+1)));
					newPoly.texVerts.add(new DXSVector2f(poly.getTexVertByID(vertices.get(v+1).vid)));
					//Save the poly
					newPolys.add(newPoly);
				} 
			} else {
				//Else we just save the poly
				newPolys.add(poly);
			}
		}
		//Replace the poly list
		primitive.polys = newPolys;
	}
	
	/**
	 * Flushes all the XML, material, and mesh data from the importer, allowing
	 * for it to be re-used. Extensions are not removed, if they should be
	 * use <code>flushExtensions()</code>
	 */
	public void flush(){
		docRoot = null;
		sceneRoot = null;
		materialList.clear();
	}
	
	/**
	 * De-registers all extensions associated with this importer.
	 */
	public void flushExtensions(){
		extensions.clear();
	}	
	
	/**
	 * Registers an extension to the importer which will be used
	 * when the importer loads model data.
	 * @param e
	 */
	public void addExtension(DXSExtension e){
		extensions.add(e);
	}
}
